package mud.player;

import java.util.ArrayList;
import java.util.List;

import mud.Combatable;
import mud.Direction;
import mud.Exit;
import mud.Interactable;
import mud.unit.Action;
import mud.unit.AttackAction;
import mud.unit.LookAction;
import mud.unit.MoveAction;

/** Enumeration of the possible player commands */
public enum Command {
    
    /** Causes a unit to look at something */
    LOOK("L") {
        @Override
        public Action interpret(PlayerCharacter unit, String cmd, String args) {

            if (args.isEmpty()) {
                return new LookAction(unit, unit.getRoom());
            } else {
                Interactable target = unit.parseTarget(args);
                if (target != null) {
                    return new LookAction(unit, target);
                } else {
                    unit.getTerminal().println(unit.getName() +
                        " cannot see anything here called \"" + args + "\".");
                    return null;
                }
            }
        }
    },
    
    /** Causes a unit to attack something */
    ATTACK("A") {
        @Override
        public Action interpret(PlayerCharacter unit, String cmd, String args) {

            if (args.isEmpty()) {
                unit.getTerminal().println("Attack what?");
                return null;
            } else {
                Interactable target = unit.parseTarget(args);
                if (target != null) {
                    if (target instanceof Combatable) {
                        return new AttackAction(unit, (Combatable) target);
                    } else {
                        unit.getTerminal().println(unit.getName() +
                            " cannot attack " + args + ".");
                        return null;
                    }
                } else {
                    unit.getTerminal().println(unit.getName() +
                        " cannot see anything here called \"" + args + "\".");
                    return null;
                }
            }
        }
    },
    
    /** Commands a unit to move through a given exit */
    MOVE("GO") {
        @Override
        public Action interpret(PlayerCharacter unit, String cmd, String args) {

            Exit exit = null;
            
            // Try to find exit by direction
            Direction direction = Direction.fromString(cmd);
            if (direction == null) {
                if (!args.isEmpty()) {
                    direction = Direction.fromString(args);
                } else {
                    unit.getTerminal().println("Move where?");
                    return null;
                }
            }
            if (direction != null) {
                exit = unit.getRoom().getExit(direction);
                if (exit == null) {
                    unit.getTerminal().println(unit.getName()
                        + " cannot go that way.");
                    return null;
                }
            } else {
                
                // Try to find exit by name
                exit = unit.parseExit(args);
                if (exit == null) {
                    unit.getTerminal().println(unit.getName() +
                        " cannot see anything here called \"" + args + "\".");
                    return null;
                }
            }
            
            return new MoveAction(unit, exit);
        }
    };

    static {
        for (Direction direction : Direction.values()) {
            MOVE.abbreviations.add(direction.name());
            MOVE.abbreviations.add(direction.getAbreviation());
        }
    }
    
    /** the capitalized shorthand versions of the command */
    private final List<String> abbreviations;

    /**
     * Creates a new command with the given abbreviations
     * @param abbreviations the capitalized shorthand versions of the command
     */
    private Command(String ... abbreviations) {
        this.abbreviations = new ArrayList<>();
        this.abbreviations.add(this.toString());
        for (String abreviation : abbreviations) {
            this.abbreviations.add(abreviation.toUpperCase());
        }
    }
    
    /**
     * Returns the command corresponding to the given string
     * @param input the string to match
     * @return the corresponding command, or null
     */
    public static Command fromString(String input) {
        for (Command command : values()) {
            if (command.abbreviations.contains(input.toUpperCase())) {
                return command;
            }
        }
        return null;
    }
    
    /**
     * Interprets user input for a given unit and returns the resulting action
     * @param unit the unit to command
     * @param cmd the player-entered command
     * @param args the arguments of the player-entered command
     * @return the resulting action
     */
    public abstract Action interpret(PlayerCharacter unit, String cmd, String args);

}